﻿namespace Game
{
    public class AI
    {
        private const int k_MaxClueBufferSize = 6;
        private static System.Collections.Generic.List<Clue> m_Clues;
        private static bool m_IsInTurn;
        private static int m_CurrentClueIndex;

        public AI()
        {
            resetAI();
        }

        public static void RegisterCardForAIPlayers(Board.Coordinate i_Coordinate, Board.Card i_Card, Board i_Board, Player[] i_PlayersToInform )
        {
            foreach (Player currentAIPlayer in i_PlayersToInform)
            {
                if (currentAIPlayer.PlayerType == Player.ePlayerType.AI)
                {
                    currentAIPlayer.AI.registerCard(i_Coordinate, i_Card, i_Board);
                }
            }
        }

        private void registerCard(Board.Coordinate i_Coordinate, Board.Card i_Card, Board i_Board)
        {
            int clueIndex;
            bool cardExistsInBuffer = isCardInClues(i_Card, out clueIndex);

            if (cardExistsInBuffer)
            {
                if (i_Board.IsCardUpturned(i_Coordinate))
                {
                    m_Clues.RemoveAt(clueIndex);
                }
                else
                {
                    addCoordToClue(i_Coordinate, clueIndex);
                }
            }
            else if (!i_Board.IsCardUpturned(i_Coordinate))
            {
                addNewClueToBuffer(i_Coordinate, i_Card);
            }            
        }

        private bool isCardInClues(Board.Card i_Card, out int o_ClueIndex)
        {            
            bool cardExists = false;
            int i = 0;
            o_ClueIndex = 0;
           
            foreach (Clue currentClue in m_Clues)
            {
                if (currentClue.Card.Equals(i_Card))
                {
                    o_ClueIndex = i;
                    cardExists = true;
                }

                i++;
            }

            return cardExists;
        }

        private void addCoordToClue(Board.Coordinate i_Coordinate, int i_ClueIndex)
        {
            if (m_Clues[i_ClueIndex].NumberOfValidCoords == 1 && !m_Clues[i_ClueIndex].Coord1.Equals(i_Coordinate))
            {
                m_Clues[i_ClueIndex].Coord2 = i_Coordinate;
                m_Clues[i_ClueIndex].NumberOfValidCoords++;
            }
        }

        private void addNewClueToBuffer(Board.Coordinate i_Coordinate, Board.Card i_Card)
        {
            if (m_Clues.Count < k_MaxClueBufferSize)
            {
                m_Clues.Add(new Clue(i_Card, i_Coordinate));
            }
        }

        public Board.Coordinate GetMove(Board i_Board, bool i_IsFirstMoveInTurn)
        {
            Board.Coordinate moveToMake;
            bool isFullClueExists;
            int fullClueIndex;

            refreshBuffer(i_Board);

            if (m_IsInTurn == false && i_IsFirstMoveInTurn)
            {
                isFullClueExists = lookForFullClue(out fullClueIndex);
                if (isFullClueExists)
                {
                    moveToMake = m_Clues[fullClueIndex].Coord1;
                    m_CurrentClueIndex = fullClueIndex;
                    m_IsInTurn = true;
                }
                else
                {
                    moveToMake = pickRandomCard(i_Board);
                }
            }
            else if (m_IsInTurn == true && i_IsFirstMoveInTurn == false)
            {
                moveToMake = m_Clues[m_CurrentClueIndex].Coord2;
                m_Clues.RemoveAt(m_CurrentClueIndex);
                m_IsInTurn = false;
                m_CurrentClueIndex = 0;
            }
            else
            {
               moveToMake = pickRandomCard(i_Board);
            }

            return moveToMake;
        }

        private void refreshBuffer(Board i_Board)
        {
            System.Collections.Generic.List<Clue> cluesToRemove = new System.Collections.Generic.List<Clue>();

            foreach (Clue currentClueInBuffer in m_Clues)
            {
                if (i_Board.IsCardUpturned(currentClueInBuffer.Coord1)) 
                {
                    cluesToRemove.Add(currentClueInBuffer);
                }
            }

            foreach (Clue currentClueToRemove in cluesToRemove)
            {
                m_Clues.Remove(currentClueToRemove);
            }
        }

        private bool lookForFullClue(out int o_FullClueIndex)
        {
            int i = 0;
            bool fullClueExists = false;
            int fullClueIndex = -1;

            foreach (Clue currentClue in m_Clues)
            {
                if (currentClue.NumberOfValidCoords == 2)
                {
                    fullClueExists = true;
                    fullClueIndex = i;       
                }

                i++;
            }

            o_FullClueIndex = fullClueIndex;

            return fullClueExists;
        }

        private Board.Coordinate pickRandomCard(Board i_Board)
        {
            Board.Coordinate[] legalMovesPool = i_Board.PoolLegalMoves();
            if (legalMovesPool.Length == 0)
            {
                throw new System.Exception("Exception: at AI.pickRandomCard() was called when no legal cards are available.\n");
            }

            System.Random random = new System.Random();
            int randomIndex = random.Next(0, legalMovesPool.Length);

            return legalMovesPool[randomIndex];
        }

        public static void ResetAI(Player[] i_AIPlayersToReset)
        {
            foreach (Player currentAIPlayer in i_AIPlayersToReset)
            {
                if (currentAIPlayer.PlayerType == Player.ePlayerType.AI)
                {
                    currentAIPlayer.AI.resetAI();
                }
            }
        }

        private void resetAI()
        {
            m_Clues = new System.Collections.Generic.List<Clue>();
            m_IsInTurn = false;
            m_CurrentClueIndex = 0;        
        }

        private class Clue
        {
            private int m_NumberOfValidCoords;
            private Board.Coordinate m_Coord1, m_Coord2;
            private Board.Card m_Card;

            public Clue(Board.Card i_Card, Board.Coordinate i_Coord1)
            {
                m_NumberOfValidCoords = 1;
                m_Coord1 = i_Coord1;
                m_Coord2 = Board.Coordinate.MakeEmpty();
                m_Card = i_Card;
            }

            public int NumberOfValidCoords
            {
                get { return m_NumberOfValidCoords; }
                set { m_NumberOfValidCoords = value; }
            }

            public Board.Coordinate Coord1
            {
                get { return m_Coord1; }
                set { m_Coord1 = value; }
            }

            public Board.Coordinate Coord2
            {
                get { return m_Coord2; }
                set { m_Coord2 = value; }
            }

            public Board.Card Card
            {
                get { return m_Card; }
                set { m_Card = value; }
            }
        }
    }
}
